#pragma once


// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

// Gizmo.h : interface of the Gizmo class
//

#include "Draggable.h"
class Color;
class GizmoManager;

// The visual representation of a gizmo has several parts. X, Y and Z
// are the three big arrows that represent the coordinate axes. XY, YZ
// and YZ represent a manipualation in the corresponding directions or
// planes, and is rendered as a diagonal segment between the big arrows.
// Finally the XYZ part is only valid for the SCALE gizmo and allows us
// to scale the three dimensions at once

BEGIN_ENUM(GizmoHandleId)
    X, Y, Z, XY, XZ, YZ, XYZ
END_ENUM(GizmoHandleId)

// ----------------------------------------------------------------------------
// Class "Gizmo"
// ----------------------------------------------------------------------------
//  Base class for the various manipulation gizmos (the frames that can be
//  dragged on the screen to transform the currently selected node)
// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

class Gizmo : public Draggable
{
public:

    Gizmo(GizmoManager* manager);
    ~Gizmo();

protected:

    // One of NodeTransform, NodePosition, NodeOrientation or NodeScale
    NodeEvent::Property m_changedProp;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    
    // The manager controls things like the type of axes or the type of pivot
    GizmoManager* m_manager;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // The gizmo frame has two components: the axes and the pivot. By "axes"
    // we mean its rotation+scale 3x3 submatrix. The axes of a given gizmo are
    // determined by the "axesType" field of the gizmoManager. For example,
    // if the manager has "axesType=WORLD" then this function will return
    // as axes the vectors (1,0,0),(0,1,0),(0,0,1). The "pivot" of a gizmo
    // matrix is computed based on the "pivotType" field of the manager. For
    // example, if the manager has "pivotType=NODE" this function queries
    // the pivot node from the manager and returns its center as the pivot
    // part of the gizmo frame
    gx::Mat4 computeGizmoFrame() const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // These two are different during a drag cycle
    gx::Mat4 m_currGizmoFrame;
    gx::Mat4 m_iniGizmoFrame;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Draw the gizmo. We pass the gizmoFrame as an argument because during a 
    // manipulation cycle we use this function to draw two copies of the gizmo
    // (the current one and the shadow that shows its initial configuration)
    void draw(View3d * view, const gx::Mat4& gizmoFrame, 
              const Color colors[3]) const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  
    // The local node transform at the time the manipulation cycle begins
    gx::Mat4 m_iniLocalTransform;

    // The global node transform at the time the manipulation cycle begins
    gx::Mat4 m_iniGlobalTransform;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Drawable overrides
    void draw(View3d* view) const override;
    size_t getDrawOrder() const override;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Draggable overrides
    bool onBeginDragImpl(const ScreenPoint & clickPoint) override;
    void onDragImpl(const ScreenPoint & dragPoint) override;
    void onEndDragImpl() override;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Virtual functions to be overriden by derived gizmo classes

    // This function does most of the work. "localTransform" is the
    // transform to be applied to the manipulated node. "gizmoTransform"
    // is the transform to be applied to the gizmo. 
    virtual void onNodeDrag(const ScreenPoint & dragPoint, 
                            gx::Mat4 & localTransform,
                            gx::Mat4 & gizmoTransform,
                            NodeEvent::Property& prop) = 0;

    // Gizmos (read: scale) that have an extra handle that controls
    // all three dimensions at once must return "true" from this member
    // function
    virtual bool hasXYZGizmoPart() const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // We draw the axes (the RGB lines) and the derived gizmos draw the 
    // end of the axes in any way they want (for instance, the rotation
    // gizmo draw the ends as circles)

    virtual void drawGizmoEnds(View3d * view, const Color colors[3], 
                                double size) const;
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Returns the screen axis (X, Y) that is more appropriate for dragging the
    // specified world axis
    Dim findPreferredScreenAxis(const gx::Vec3& axis) const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // A helper function that queries the gizmoManager to determined whether
    // a given dimension (X,Y or Z) should be manipulated
    bool isDimSelected(Dim dim) const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    friend class GizmoManager;
};

